﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;

namespace Weazel.Gezel.Model.Test
{
	using Mod = Weazel.Gezel.Model;
	using Weazel.Gezel.Parser;
	using Weazel.Gezel.Model.Errors;

	[TestClass]
	public class TestExpressionVerification
	{
		[TestMethod]
		public void TestIdentifierExpressionReferencedEntity()
		{
			const string modelDescription = @"
        dp mydp {
          sig register : ns(8);
          
          sfg exec1 {
            register = 1; 
          }

          sfg exec2 {
            register = 2; 
          }
        }
        
        sequencer myseq(mydp) { exec1; exec2; }

        system T { mydp; }
      ";

			List<ParseError> parseErrors = new List<ParseError>();
			List<VerificationError> verificationErrors = null;
			List<VerificationWarning> warnings = null;

			Model model = ParseModel.Parse(ref parseErrors, modelDescription);

			Assert.AreEqual(0, parseErrors.Count);

			model.Verify(ref verificationErrors, ref warnings);

			Assert.AreEqual<int>(verificationErrors.Count, 0);

			BinaryExpression bExpr = model.Datapaths["mydp"].SignalFlowGraphs["exec1"].Expressions[0] as BinaryExpression;
			Assert.IsNotNull(bExpr);

			LeafExpression lExpr = bExpr.LeftChild as LeafExpression;
			Assert.IsNotNull(lExpr);

			// here we check that the target of the identifier expression is correct
			Assert.IsNotNull(lExpr.ReferencedEntity);
			Assert.IsTrue(lExpr.ReferencedEntity is CommonEntity);
			Assert.AreEqual<string>((lExpr.ReferencedEntity as CommonEntity).LocalName, lExpr.Value);
			Assert.AreEqual<System.Type>(lExpr.ReferencedEntity.GetType(), typeof(Signal));
		}

		[TestMethod]
		public void TestSignalDefiners()
		{
			const string modelDescription = @"
        dp mydp {
          sig register : ns(8);
          
          sfg exec1 {
            register = 1; 
          }

          sfg exec2 {
            register = 2; 
          }
        }
        
        sequencer myseq(mydp) { exec1; exec2; }

        system T { mydp; }
      ";

			List<ParseError> parseErrors = new List<ParseError>();
			List<VerificationError> verificationErrors = null;
			List<VerificationWarning> warnings = null;

			Model model = ParseModel.Parse(ref parseErrors, modelDescription);

			Assert.AreEqual(0, parseErrors.Count);

			model.Verify(ref verificationErrors, ref warnings);

			Assert.AreEqual<int>(verificationErrors.Count, 0);

			Signal signal = model.Datapaths["mydp"].Signals["register"];
			Assert.AreEqual<string>(signal.Name, "mydp.register");

			Assert.AreEqual<int>(signal.Definers.Count, 2);
			Assert.AreEqual<string>((signal.Definers[0] as LeafExpression).Value, "1");
			Assert.AreEqual<string>((signal.Definers[1] as LeafExpression).Value, "2");

			Assert.AreEqual<string>(signal.DefiningSignalFlowGraphs[signal.Definers[0]].Name, "mydp.exec1");
			Assert.AreEqual<string>(signal.DefiningSignalFlowGraphs[signal.Definers[1]].Name, "mydp.exec2");
		}

		[TestMethod]
		public void TestRegisterDefiners()
		{
			const string modelDescription = @"
        dp mydp {
          reg register : ns(8);
          
          sfg exec1 {
            register = 1; 
          }

          sfg exec2 {
            register = 2; 
          }
        }
        
        sequencer myseq(mydp) { exec1; exec2; }

        system T { mydp; }
      ";

			List<ParseError> parseErrors = new List<ParseError>();
			List<VerificationError> verificationErrors = null;
			List<VerificationWarning> warnings = null;

			Model model = ParseModel.Parse(ref parseErrors, modelDescription);

			Assert.AreEqual(0, parseErrors.Count);

			model.Verify(ref verificationErrors, ref warnings);

			Assert.AreEqual<int>(verificationErrors.Count, 0);

			Register register = model.Datapaths["mydp"].Registers["register"];
			Assert.AreEqual<string>(register.Name, "mydp.register");

			Assert.AreEqual<int>(register.Definers.Count, 2);
			Assert.AreEqual<string>((register.Definers[0] as LeafExpression).Value, "1");
			Assert.AreEqual<string>((register.Definers[1] as LeafExpression).Value, "2");

			Assert.AreEqual<string>(register.DefiningSignalFlowGraphs[register.Definers[0]].Name, "mydp.exec1");
			Assert.AreEqual<string>(register.DefiningSignalFlowGraphs[register.Definers[1]].Name, "mydp.exec2");
		}

		[TestMethod]
		public void TestOutPortDefiners()
		{
			const string modelDescription = @"
        dp mydp(out outport : ns(8)) {
          
          sfg exec1 {
            outport = 1; 
          }

          sfg exec2 {
            outport = 2; 
          }
        }
        
        sequencer myseq(mydp) { exec1; exec2; }

        system T { mydp; }
      ";

			List<ParseError> parseErrors = new List<ParseError>();
			List<VerificationError> verificationError = null;
			List<VerificationWarning> warnings = null;

			Model model = ParseModel.Parse(ref parseErrors, modelDescription);

			Assert.AreEqual(0, parseErrors.Count);

			model.Verify(ref verificationError, ref warnings);

			Assert.AreEqual<int>(verificationError.Count, 0);

			OutPort outport = model.Datapaths["mydp"].OutPorts["outport"];
			Assert.AreEqual<string>(outport.Name, "mydp.outport");

			Assert.AreEqual<int>(outport.Definers.Count, 2);
			Assert.AreEqual<string>((outport.Definers[0] as LeafExpression).Value, "1");
			Assert.AreEqual<string>((outport.Definers[1] as LeafExpression).Value, "2");

			Assert.AreEqual<string>(outport.DefiningSignalFlowGraphs[outport.Definers[0]].Name, "mydp.exec1");
			Assert.AreEqual<string>(outport.DefiningSignalFlowGraphs[outport.Definers[1]].Name, "mydp.exec2");
		}

		[TestMethod]
		public void TestExpressionBitWidth()
		{
			Tokenizer t = new Tokenizer("3 + 1");

			List<ParseError> parseErrors = new List<ParseError>();
			List<VerificationWarning> warnings = null;

			Expression expr = ParseExpression.Parse(new Set<Token>(Token.Eof), t);

			Assert.AreEqual(0, parseErrors.Count);

			List<VerificationError> errors = new List<VerificationError>();
			expr.Verify(ref errors, ref warnings);

			Assert.IsTrue(errors.Count == 0);

			Assert.IsTrue(expr is BinaryExpression);
			// bitwidth should be two since
			// the constant is unsigned and
			// three can be represented with 
			// just two bits
			Assert.AreEqual<int>(2, expr.Width);
		}
	}
}